iT邦幫忙

2023 iThome 鐵人賽

DAY 27
0
自我挑戰組

JS 加強筆記系列 第 27

Day 27:如何中斷 forEach

  • 分享至 

  • xImage
  •  

今天不寫 promise,換個口味寫寫 JS 的陣列方法 forEach

之前在面試的時候被問到這個問題:可以中斷 forEach 嗎?當時不太肯定,但心裡想的是,既然這個方法是語意化的做迴圈的事情,那總有類似迴圈中斷的功能吧!?

後來才知道,答案是:不行。或者說硬要也是有辦法,但不行是比較好的結論。

嘗試中斷

break

說到中斷迴圈首先想到 break,但在這裡並不適用。這部分還好理解,因為 break 是用來中斷迴圈或 switch,中斷後 control 會去到後面的陳述式。

既然不能使用 break,那如果查查看 forEach 本身有沒有中斷的方式呢?就會看到 MDN 上這樣寫:「中斷 forEach 的唯一辦法是丟出錯誤,但如果有需要這樣做,就代表 forEach 是錯誤的選擇。」非常直接的告訴你,要中斷請換方法。

雖然如此,換方法前還是可以看一下在 forEach 中的一些行為:

return

const array = [1, 2, 3, 4]
array.forEach((element) => {
    if (element === 2) {
        return;
    }
    console.log(element);
});

// 1, 3, 4 

這樣會從 forEach 的 callback 中 return,可以有像迴圈中使用 continue 的效果。

丟出錯誤

const array = [1, 2, 3, 4]
try {
    array.forEach((element) => {
        if (element === 2) {
            throw new Error("Break the loop.")
        }
        console.log(element);
    })
} catch (error) {
     console.error(error);
}

// 印出 1 之後會報錯

如上面所寫,是可以讓 forEach 停下來沒錯,但用錯誤處理來作為程式邏輯不是理想的方法。

forEach 的替代方案

假設有中斷的需求,想改用別的方法,第一種選項是樸實無華的 for 迴圈 (或是 for...infor...of 等其他迴圈方式),雖然不像陣列方法簡潔但很可靠。另外當碰到一些限制 (例如 forEach 不接受非同步函式),也可以考慮使用迴圈。

再來也可以依需求情境選擇其他陣列方法:

找出陣列中特定值

find()findIndex() 這兩個方法都會對陣列的每個元素執行 callback,並回傳第一個符合的元素/ 元素索引值,並且有符合的值就會中斷對剩下元素的操作。如果都不符合, find() 會回傳 undefinedfindIndex() 會回傳 -1。

// 12
[5, 12, 8, 130, 44].find(element => element > 10);

// 2
[1, 2, 3].findIndex(number => number === 3)

確認陣列是否包含特定值

some()every() 可以用來檢查陣列中是否有特定目標,兩者都會對陣列的每個元素執行 callback,並回傳布林值,差別在於:

  • some()任一個元素回傳 true 時就會回傳 true 並中斷,都不符合則回傳 false。也就是代表至少有一個符合的元素。
  • every() 則是所有元素都符合才回傳 true,只要任一個元素為 false 就回傳 false。也就是適用需要全部都符合的情境。
// true
[1, 2, 3].some(number => number < 2)

// false
[1, 2, 3].every(number => number < 2)

看過有教學寫說可以利用 「some() 回傳 true 會中斷、every() 回傳 false 會中斷」的特性,用回傳布林來終止陣列迭代。運作上沒錯,但個人覺得這樣是本末倒置,而且浪費方法本身的語意、不見得好讀,還不如使用 for 迴圈。


補充找資料過程中看到一篇有趣的文章。文章的前提有點驚人:作者有次當面試官,碰到一個無法回答 while 迴圈問題的中階應徵者,而對方的理由是他只寫宣告式 (declarative) 程式碼,對於指令式 (imperative) 程式不屑一顧。以此為出發點,作者做了一些實驗來比較 map, reduce, forEach 這些方法和 for 迴圈的效能表現。雖然效能並非唯一指標,作者也很中肯的說在多數專案這些方法差異不大,但文章中有些不錯的建議 (例如很多時候不妨以 for..of 取代 forEach,兼具效能及可讀性),然後延伸到宣告式與指令式程式的比較,可供額外思考。

其他參考資料:
https://webtips.dev/break-from-foreach-in-javascript
https://www.tutorialspoint.com/how-to-stop-foreach-method-in-javascript
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach
https://www.freecodecamp.org/news/javascript-loops-label-statement-continue-statement-and-break-statement-explained/


上一篇
Day 26:promise 實作 (4)
下一篇
Day 28:macrotask 與 microtask (1)
系列文
JS 加強筆記30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言